/**
 *	@file	cursor_mv.c
 *	@brief	選択カーソル表示/コントロールマネージャ
 *	@author	Miyuki Iwasawa
 *	@date	07.08.21
 */

#include "common.h"
#include "system/arc_util.h"
#include "system/arc_tool.h"
#include "system/clact_util.h"
#include "system/clact_tool.h"
#include "system/render_oam.h"

#define __CURSOR_MV_H_GLOBAL
#include "system/cursor_mv.h"

/**
 *	@brief	カーソル移動制御マネージャワーク作成
 *
 *	@param	num	確保するパラメータワーク数
 *	@param	heapID	利用するheapID
 */
CURSORMV_MANAGER* CursorMvMan_Create(u16 num,int heapID)
{
	CURSORMV_MANAGER* wk;

	//管理領域確保
	wk = sys_AllocMemory(heapID,sizeof(CURSORMV_MANAGER));
	MI_CpuClear8(wk,sizeof(CURSORMV_MANAGER));

	//指定数分のワーク領域を確保
	wk->all_num = num;
	wk->ary = sys_AllocMemory(heapID,sizeof(CURSORMV_PARAM)*num);
	MI_CpuClear8(wk->ary,sizeof(CURSORMV_PARAM)*num);

	return wk;
}

/*
 *	@brief	カーソル移動制御マネージャ　削除
 */
void CursorMvMan_Release(CURSORMV_MANAGER* wk)
{
	int i;

	//未開放のパラメータがあれば開放
	for(i = 0;i < wk->all_num;i++){
		if(!wk->ary[i].valid || (wk->ary[i].dat == NULL)){
			continue;
		}
		CursorMvMan_DeleteParam(wk,i);
	}

	MI_CpuClear8(wk->ary,sizeof(CURSORMV_PARAM)*wk->all_num);
	sys_FreeMemoryEz(wk->ary);

	MI_CpuClear8(wk,sizeof(CURSORMV_MANAGER));
	sys_FreeMemoryEz(wk);
	wk = NULL;
}

/**
 *	@brief	カーソル移動パラメータ登録(Unitタイプ)
 *
 *	@param	wk	マネージャポインタ
 *	@param	dat	カーソル移動座標パラメータ配列へのポインタ
 *	@param	ini_pos	初期カーソルポジションidx
 *	@param	oam_mode	OAMのワークタイプ CURMV_OAM_CLACT or CURMV_OAM_CATS
 *	@param	cur_lt	左上カーソルOAMワーク(void*にキャストして引き渡す)
 *	@param	cur_lb	左下カーソルOAMワーク
 *	@param	cur_rt	右上カーソルOAMワーク
 *	@param	cur_rb	右下カーソルOAMワーク
 *
 *	＊内部でワークをAllocするので、必ずDeleteParamで開放すること
 */
u16 CursorMvMan_EntryParamUnit(CURSORMV_MANAGER* wk,const CURSORMV_DAT* dat,u8 setnum,u8 ini_pos,
	CURMV_OAM_CODE oam_mode,int heapID,
	void* cur_lt,void* cur_lb,void* cur_rt,void* cur_rb )
{
	u16	idx,all;
	CURSORMV_PARAM* cp;

	idx = CursorMvMan_GetEnptyWorkNo(wk);
	if(idx == 0xFFFF){
		return 0xFFFF;	//空きがない
	}
	cp = &(wk->ary[idx]);

	cp->valid = 1;
	cp->type = CURMV_TYPE_UNIT;
	cp->num = setnum;
	cp->max = cp->num-1;

	//配列データをコピー
	cp->dat = sys_AllocMemory(heapID,sizeof(CURSORMV_DAT)*cp->num);
	MI_CpuCopy8(dat,cp->dat,sizeof(CURSORMV_DAT)*cp->num);

	//初期カーソルポイントセット
	if(ini_pos >= cp->num){
		cp->pos = 0;
	}else{
		cp->pos = ini_pos;
	}

	//oamモードをセット
	cp->oam_mode = oam_mode;

	//利用カーソルオブジェをセット
	cp->cur_lt.oam_p = cur_lt;
	cp->cur_lb.oam_p = cur_lb;
	cp->cur_rt.oam_p = cur_rt;
	cp->cur_rb.oam_p = cur_rb;

	return idx;
}

/**
 *	@brief	登録されているワークパラメータを削除する
 *
 *	@param	idx	削除したいワークidx
 *
 *	@return	削除できたらTRUE
 */
BOOL CursorMvMan_DeleteParam(CURSORMV_MANAGER* wk,u16 idx)
{
	if(idx >= wk->all_num || wk->ary[idx].valid == 0){
		return FALSE;
	}
	if(wk->pc_idx == idx){
		wk->pc_idx = 0xFFFF;
		wk->pc = NULL;
	}
	//データ配列バッファを開放
	MI_CpuClear8(wk->ary[idx].dat,sizeof(CURSORMV_DAT)*wk->ary[idx].num);
	sys_FreeMemoryEz(wk->ary[idx].dat);
	MI_CpuClear8(&(wk->ary[idx]),sizeof(CURSORMV_PARAM));
	return FALSE;
}

/**
 *	@brief	空いているワークナンバーを返す
 *
 *	@retval	0xFFFF 空いているワークがない
 *	@retval	それ以外 ワークIdx
 */
u16	CursorMvMan_GetEnptyWorkNo(CURSORMV_MANAGER* wk)
{
	u16 i;

	for(i = 0;i < wk->all_num;i++){
		if(wk->ary[i].valid == 0){
			return i;
		}
	}
	return 0xFFFF;
}

/**
 *	@brief	指定したワークのカーソルを表示/非表示切替
 *
 *	@param	idx	指定ワークidx(CURMV_CURRENTを指定した場合、カレントを操作する)
 *	@param	flag	TRUE:描画、FALSE:非描画
 *
 *	@return	操作したワークidx
 */
u16 CursorMvMan_SetCursorDrawFlag(CURSORMV_MANAGER* wk,u16 idx,BOOL flag)
{
	CURSORMV_PARAM* pc;

	if(idx == CURMV_CURRENT){
		pc = wk->pc;
	}else{
		if(idx >= wk->all_num || wk->ary[idx].valid == 0){
			return 0xFFFF;
		}
		pc = &(wk->ary[idx]);
	}

	if(pc->oam_mode == CURMV_OAM_CLACT){
		CATS_ObjectEnable(pc->cur_lt.clact_p,flag);
		if(pc->type == CURMV_TYPE_UNIT){
			CATS_ObjectEnable(pc->cur_lb.clact_p,flag);
			CATS_ObjectEnable(pc->cur_rt.clact_p,flag);
			CATS_ObjectEnable(pc->cur_rb.clact_p,flag);
		}
	}else{
		CATS_ObjectEnableCap(pc->cur_lt.cats_p,flag);
		if(pc->type == CURMV_TYPE_UNIT){
			CATS_ObjectEnableCap(pc->cur_lb.cats_p,flag);
			CATS_ObjectEnableCap(pc->cur_rt.cats_p,flag);
			CATS_ObjectEnableCap(pc->cur_rb.cats_p,flag);
		}
	}
	return idx;
}

/**
 *	@brief	現在のポジションにOAM位置を再設定
 *
 *	@param	idx	ワークindex(CURMV_CURRENT=0xFFFF)を指定した場合、カレントを操作
 */
static void cmv_SetOamPos(CURSORMV_MANAGER* wk,u16 idx)
{
	CURSORMV_PARAM *pp;
	const CURSORMV_DAT* pd;

	if(idx == CURMV_CURRENT){
		pp = wk->pc;
	}else{
		if(idx >= wk->all_num){
			return;	//なにもしない
		}
		pp = &(wk->ary[idx]);
	}
	pd = &(pp->dat[pp->pos]);

	//新ポジションにカーソルOBJを移動
	if(pp->oam_mode == CURMV_OAM_CLACT){
		if(pp->type == CURMV_TYPE_ONE){
			CATS_ObjectPosSet(pp->cur_lt.clact_p,pd->px,pd->py);
		}else{
			CATS_ObjectPosSet(pp->cur_lt.clact_p,pd->px+pd->ofs_l,pd->py+pd->ofs_u);
			CATS_ObjectPosSet(pp->cur_lb.clact_p,pd->px+pd->ofs_l,pd->py+pd->ofs_d);
			CATS_ObjectPosSet(pp->cur_rt.clact_p,pd->px+pd->ofs_r,pd->py+pd->ofs_u);
			CATS_ObjectPosSet(pp->cur_rb.clact_p,pd->px+pd->ofs_r,pd->py+pd->ofs_d);
		}
	}else{
		if(pp->type == CURMV_TYPE_ONE){
			CATS_ObjectPosSetCap(pp->cur_lt.cats_p,pd->px,pd->py);
		}else{
			CATS_ObjectPosSetCap(pp->cur_lt.cats_p,pd->px+pd->ofs_l,pd->py+pd->ofs_u);
			CATS_ObjectPosSetCap(pp->cur_lb.cats_p,pd->px+pd->ofs_l,pd->py+pd->ofs_d);
			CATS_ObjectPosSetCap(pp->cur_rt.cats_p,pd->px+pd->ofs_r,pd->py+pd->ofs_u);
			CATS_ObjectPosSetCap(pp->cur_rb.cats_p,pd->px+pd->ofs_r,pd->py+pd->ofs_d);
		}
	}
}

/*
 *	@brief	現在のカレントのパラメータを切り替える
 *
 *	@param	wk	マネージャーワーク
 *	@param	idx	カレント指定したいワークのidx
 *	@param	ini_pos 起動時の初期カーソル位置
 *
 *	@retval	0xFFFF エラー
 *	@retval それ以外 カレント指定されたワークidx
 */
u16 CursorMvMan_SetCurrent(CURSORMV_MANAGER* wk,u16 idx,u8 ini_pos)
{
	if(idx >= wk->all_num){
		return 0xFFFF;
	}
	wk->pc = &(wk->ary[idx]);
	wk->pc_idx = idx;
	if(ini_pos != 0xFF){
		if(ini_pos >= wk->ary[idx].num){
			wk->ary[idx].pos = 0;
		}else{
			wk->ary[idx].pos = ini_pos;
		}
	}

	//ポジションセット
	cmv_SetOamPos(wk,CURMV_CURRENT);
	return idx;
}

/**
 *	@brief	現在のカレントカーソルのポジションを取得する
 */
u8 CursorMvMan_GetPos(CURSORMV_MANAGER* wk)
{
	return wk->pc->pos;
}

/**
 *	@brief	指定indexパラメータのカーソルポジションを取得する
 *
 *	@param	idx	CURMV_CURRENTを指定した場合、カレントのポジションを取得する
 */
u8 CursorMvMan_GetPosIdx(CURSORMV_MANAGER* wk,u16 idx)
{
	if(idx == CURMV_CURRENT){
		return wk->pc->pos;
	}
	if(idx >= wk->all_num || wk->ary[idx].valid == 0){
		return 0;
	}
	return wk->ary[idx].pos;
}

/**
 *	@brief	カーソルを進めた先のポジションを取得
 */
static void cmv_GetNextPos(CURSORMV_MANAGER* wk,u8 dir)
{
	const CURSORMV_DAT* pd;
	u8	pos;

	//カレントが未登録ならなにもしない
	if(wk->pc == NULL){
		return;
	}
	pd = &(wk->pc->dat[wk->pc->pos]);

	switch(dir){
	case 1:
		pos = pd->right;
		break;
	case 2:
		pos = pd->up;
		break;
	case 3:
		pos = pd->down;
		break;
	case 0:
	default:
		pos = pd->left;
		break;
	}
	if(pos < 0 || pos > wk->pc->max){
		return;	//なにもしない
	}
	wk->pc->pos = pos;
}


/**
 *	@brief	カレントカーソルポジションを進める
 *
 *	@param	dir	進める方向
 */
u8 CursorMvMan_MovePos(CURSORMV_MANAGER* wk,u8 dir)
{
	//次のポジションを計算
	cmv_GetNextPos(wk,dir);

	//新しい位置にOAMを表示
	cmv_SetOamPos(wk,CURMV_CURRENT);

	return wk->pc->pos;
}

/*
 *	@brief	カレントのカーソルポジションをセットする
 *
 *	@param	pos カーソル位置
 */
u8 CursorMvMan_SetPos(CURSORMV_MANAGER* wk,u8 pos)
{
	return CursorMvMan_SetPosIdx(wk,wk->pc_idx,pos);
}

/**
 *	@brief	index指定したパラメータのカーソルポジションをセットする
 *
 *	@param	wk	マネージャーワーク
 *	@param	idx	カレント指定したいワークのidx(CURMV_CURRENTを指定した場合、カレントを操作する)
 *	@param	pos カーソル位置
 *
 *	@return	設定されたカーソルポジションNo
 */
u8 CursorMvMan_SetPosIdx(CURSORMV_MANAGER* wk,u16 idx,u8 pos)
{
	CURSORMV_PARAM* pp;

	if(idx == CURMV_CURRENT){
		pp = wk->pc;
		idx = wk->pc_idx;
	}else{
		if(idx >= wk->all_num || wk->ary[idx].valid == 0){
			return 0;
		}
		pp = &(wk->ary[idx]);
	}

	if(pp->max < pos){
		pp->pos = 0;
	}else{
		pp->pos = pos;
	}
	//新しい位置にOAMを表示
	cmv_SetOamPos(wk,idx);

	return pp->pos;
}

/**
 *	@brief	カーソルのアニメ状態を制御するセットする
 *
 *	@param	wk	マネージャーワーク
 *	@param	idx	アニメ状態を操作したいワークのidx(CURMV_CURRENTを指定すれば、カレントを操作する)
 *	@param	TRUEならアニメ開始、FALSEならストップ
 *
 *	@return	設定されたカーソルポジションNo
 */
void CursorMvMan_SetAnmFlagIdx(CURSORMV_MANAGER* wk,u16 idx,BOOL flag)
{
	int i;
	CURSORMV_PARAM* pp;

	if(idx == CURMV_CURRENT){
		idx = wk->pc_idx;
	}
	if(idx >= wk->all_num || wk->ary[idx].valid == 0){
		return;
	}
	pp = &wk->ary[idx];

	if(pp->oam_mode == CURMV_OAM_CLACT){
		if(pp->type == CURMV_TYPE_ONE){
			CLACT_AnmReStart(pp->cur_oam[0].clact_p);
			CATS_ObjectAutoAnimeSet(pp->cur_oam[0].clact_p,flag);
		}else{
			for(i = 0;i < 4;i++){
				CLACT_AnmReStart(pp->cur_oam[i].clact_p);
				CATS_ObjectAutoAnimeSet(pp->cur_oam[i].clact_p,flag);
			}
		}
	}else{
		if(pp->type == CURMV_TYPE_ONE){
			CATS_ObjectAnimeReStartCap(pp->cur_oam[0].cats_p);
			CATS_ObjectAutoAnimeSetCap(pp->cur_oam[0].cats_p,flag);
		}else{
			for(i = 0;i < 4;i++){
				CATS_ObjectAnimeReStartCap(pp->cur_oam[i].cats_p);
				CATS_ObjectAutoAnimeSetCap(pp->cur_oam[i].cats_p,flag);
			}
		}
	}
}